package webserver;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import utils.Utils;
import webserver.iterfaces.IHttpResponse;
import webserver.response.HttpByteResponse;
import webserver.response.HttpTextResponse;

import compiller.CompileCodeFromPage;

import dao.DAO;

public class PageDispatcher {

    private static final String DEFAULT_OS_DELIMITER = "/";
    private static File file = new File(Consts.PATH_TO_WEB);

    private CompileCodeFromPage compiller = new CompileCodeFromPage(
	    DAO.getInstance());

    public IHttpResponse dispatch(HttpRequest rq) {
	if (rq == null)
	    throw new Error();
	if (rq.getPage() == null) {
	    throw new Error();
	}
	String pageName = rq.getPage().replace("\\", DEFAULT_OS_DELIMITER);
	if (!pageName.equals(Consts.INDEX)) {
	    String ext;
	    ext = getExt(pageName);
	    String type = Consts.MIME_TYPES.get(ext);
	    if (type != null) {
		return processNotIndex(rq, pageName, type);
	    } else {
		return error("No right file type");
	    }
	} else {
	    return processIndex(rq);
	}
    }

    private String getExt(String pageName) {
	String[] extArray = pageName.split("\\.");
	if (extArray.length > 1) {
	    return "." + extArray[extArray.length - 1];
	} else {
	    return null;
	}
    }

    private IHttpResponse processIndex(HttpRequest rq) {
	File[] listFiles = file.listFiles(new FileFilter() {

	    @Override
	    public boolean accept(File file) {
		return file.getName().startsWith("index") && file.isFile();
	    }
	});
	if (listFiles != null && listFiles.length > 0) {
	    File index = listFiles[0];
	    return processResult(rq, index);
	} else {
	    return error("No index found");
	}
    }

    private IHttpResponse processNotIndex(HttpRequest rq, String pageName,
	    String type) {
	String[] split = pageName.split(DEFAULT_OS_DELIMITER);
	File currentFile = file;
	for (final String fName : split) {
	    File[] listFiles = currentFile.listFiles(new FilenameFilter() {
		@Override
		public boolean accept(File dir, String name) {
		    return name.equals(fName);
		}
	    });
	    if (listFiles.length > 0) {
		currentFile = listFiles[0];
	    } else {
		return error("Not Found");
	    }
	}
	return processResult(rq, currentFile);
    }

    private static final Pattern JAVA_SECTION = Pattern.compile(
	    "<\\?java (.*?)\\?java>", Pattern.DOTALL | Pattern.MULTILINE);

    private IHttpResponse processResult(HttpRequest rq, File file) {
	if (file.isFile()) {
	    String ext = getExt(file.getPath());
	    String contentType = Consts.MIME_TYPES.get(ext);
	    if (contentType == null) {
		contentType = Consts.MIME_TYPES.get(".html");
	    }
	    if (contentType.contains("text")) {
		return loadTextPage(rq, file, ext, contentType);
	    } else {
		return loadImgPage(rq, file, ext, contentType);
	    }
	} else {
	    return error("Not file");
	}
    }

    private IHttpResponse loadImgPage(HttpRequest rq, File file, String ext,
	    String contentType) {
	HttpByteResponse response = new HttpByteResponse();
	response.setContentType(contentType);
	return response.setResultPage(Utils.getBytesFromFile(file));
    }

    private IHttpResponse loadTextPage(HttpRequest rq, File file, String ext,
	    String contentType) {
	HttpTextResponse httpResponse = new HttpTextResponse();
	httpResponse.setContentType(contentType);
	String result = Utils.deserializeString(file);
	if (ext.equals(Consts.COMPILED_PAGE_EXTENSION)) {
	    Matcher matcher = JAVA_SECTION.matcher(result);
	    StringBuffer sb = new StringBuffer();
	    while (matcher.find()) {
		String javaCode = matcher.group(1);
		String invokedResult;
		try {
		    invokedResult = compiller.invoke(javaCode, rq);
		} catch (Exception e) {
		    e.printStackTrace();
		    throw new RuntimeException(e);
		}
		matcher.appendReplacement(sb, invokedResult);
	    }
	    matcher.appendTail(sb);
	    result = sb.toString();
	}
	return httpResponse.setResultPage(result);
    }

    private IHttpResponse error(String msg) {
	return new HttpTextResponse().setCode(404).setResultPage(msg);
    }
}
